#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <vector>

#define THREAD_NUM 2

class RingQueue
{
    public:
        RingQueue()
            :vec_(4)
        {
            capacity_ = 4;
            sem_init(&lock_, 0, 1);
            sem_init(&read_, 0, 0);
            sem_init(&write_, 0, 4);

            w_pos_ = 0;
            r_pos_ = 0;
        }

        ~RingQueue()
        {
            sem_destroy(&lock_);
            sem_destroy(&read_);
            sem_destroy(&write_);
        }

        void Push(int data)
        {
            sem_wait(&write_);

            sem_wait(&lock_);
            vec_[w_pos_] = data;
            w_pos_ = (w_pos_ + 1) % capacity_;
            sem_post(&lock_);

            sem_post(&read_);
        }

        void Pop(int* data)
        {
            sem_wait(&read_);

            sem_wait(&lock_);
            *data = vec_[r_pos_];
            r_pos_ = (r_pos_ + 1) % capacity_;
            sem_post(&lock_);

            sem_post(&write_);
        }
    private:
        std::vector<int> vec_;
        size_t capacity_;

        // 互斥
        sem_t lock_;

        //同步
        sem_t read_;
        sem_t write_;

        int w_pos_;
        int r_pos_;
};

void* ReadStart(void* arg)
{
    RingQueue* rq = (RingQueue*)arg;
    while(1)
    {
        int data;
        rq->Pop(&data);
        printf("i am ReadStart %p, i consume %d\n", pthread_self(), data);
    }
    return NULL;
}

void* WriteStart(void* arg)
{
    RingQueue* rq = (RingQueue*)arg;
    int data = 1;
    while(1)
    {
        rq->Push(data++);
        sleep(1);

    }
    return NULL;
}

int main()
{
    RingQueue* rq = new RingQueue();
    if(rq == NULL)
    {
        return 0;

    }

    pthread_t re[THREAD_NUM], wr[THREAD_NUM];
    for(int i = 0; i < THREAD_NUM; i++)
    {
        int ret = pthread_create(&re[i], NULL, ReadStart, (void*)rq);
        if(ret < 0)
        {
            perror("pthread_create");
            return 0;

        }
        ret = pthread_create(&wr[i], NULL, WriteStart, (void*)rq);
        if(ret < 0)
        {
            perror("pthread_create");
            return 0;

        }

    }

    for(int i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(re[i], NULL);
        pthread_join(wr[i], NULL);

    }
    return 0;
}
